// 封装axios
import axios from 'axios'
// import Vue from 'vue'
// 引入toase 这里Toase===this.$toase
import { Toast } from 'vant'
// 导入vuex实例化对象
import Store from '@/store'
// 导入路由的实例化对象
import Router from '@/router'

import { removeToken, getToken } from '@/utils/token.js'

// 配置基地址
// axios.defaults.baseURL = process.env.VUE_APP_URL
// 抽离api
// create是创建的意思
// _fetch我们起的变量名相当于是返回一个更改后的axios
const _fetch = axios.create({
  baseURL: process.env.VUE_APP_URL
})
// 取消请求的数组
let cancelArr = []
// 将取消的方法添加到window里
// 第一个参数传值接口的url,第二个参数cancelAll所有的取消方法,传值是false就不是全局取消,true就是全局取消
window.cancelEvent = function (url, cancelAll) {
  cancelArr = cancelArr.filter((item, index) => {
    if (cancelAll) {
      // 所有的接口都调用取消方法同时删除所有
      item.fn()
      return false
    } else {
      if (item.url === url) {
        // 如果URL相同就调用取消方法同时删除该项
        item.fn()
        return false
      } else {
        // 接口不一样不做处理
        return true
      }
    }
  })
}

// 请求拦截
_fetch.interceptors.request.use(
  function (config) {
    // 调用取消方法传值url(不是全局取消)
    // try{}catch(){}包住可能会出错
    try {
      window.cancelEvent(config.url, false)
    } catch (err) {
      console.log(err)
    }

    // console.log('s', config)
    // 有部分开发人员,多传参数也ok,一部分开发写代码很严格,多传参数就会报错,所以我们在需要请求头的配置里加上needToken:true
    // 这个needToken也会响应在config里面,我们判断是true就需要请求头,传参,不需要就不传
    if (config.needToken) {
      config.headers.authorization = `Bearer ${getToken()}`
    }
    // 取消方法,全局添加cancelToken
    config.cancelToken = new axios.CancelToken(function (cancel) {
      cancelArr.push({
        // 数组储存所有的取消方法
        fn: cancel,
        // 存储对应接口的url
        url: config.url
      })
    })
    return config
  },
  function (error) {
    return Promise.reject(error)
  }
)
// 响应拦截器
_fetch.interceptors.response.use(
  function (res) {
    // 看看响应回来什么
    // console.log('响应回来', res)
    // 如果回来的是200return res
    if (res.data.code === 200) {
      return res
    } else {
      // 路由过期处理
      if (res.data.code === 401 || res.data.code === 403) {
        // 如果响应回来config里 onError是true代表不处理这个错误反之处理
        if (res.config.onError) {
          removeToken()
          // 中止.then
          return Promise.reject(new Error(res.data.message))
        } else {
          // 提示过期
          Toast.fail('请重新登陆')
          // 删除token
          removeToken()
          console.log(123)
          // 修改isLogin
          Store.commit('setIsLogin', true)
          // 跳转路由,跳回原来token有问题的或出错的网址
          // window.location.href当前网址,我们只要#号后面的参数,split截取
          Router.push('/login?redirect=' + window.location.href.split('#')[1])
          // 中止.then执行
          return Promise.reject(new Error(res.data.message))
        }
      } else {
        // 但是如果响应回来的不是200要怎么做？
        // 提示错误,注意这里this不是指向vue
        console.log(res)
        Toast.fail(res.data.message)
        // 光有提示不够他还会继续执行.then后的内容,我们要把错误抛出去,让catch捕获到执行
        // 中止.then的执行,跳转到catch执行
        return Promise.reject(new Error(res.data.message))
      }
    }
  },
  function (error) {
    return Promise.reject(error)
  }
)

// 挂载到vue上,实际上我们一般不会挂载到原型上,而是暴露出去
// Vue.prototype.$axios = axios

export default _fetch
